require "lib/wave"

module MediaTools
  
  class ContainerFactory
    def self.register_container_class(container_class)
      (@@containers ||= []) << container_class
    end

    def self.container_for_file(path)
      container_cls = @@containers.find do |cls|
        cls.uses_extension File.extname(path)
      end
      raise RuntimeError.new("cannot find suitable container for file: #{path}") unless container_cls
      container_cls.new(path)
    end
  end
    
  module Containers    
    module Container
      def self.included(othermod)
        ContainerFactory.register_container_class(othermod) if othermod.instance_of? Class
        othermod.extend(ClassMethods)
      end

      module ClassMethods
        def extension(extension)
          (@extensions ||= []) << extension
        end

        def uses_extension needed_ext
          @extensions.find do |ext|
            needed_ext == ext
          end        
        end
      end

      def initialize(filepath = nil, should_read_format = true)
        if filepath
          @filepath = filepath
          @format = read_format if should_read_format
        end
        yield self if block_given?
      end
      attr :filepath
      attr :format, true
      
      def ==(other)
        self.class == other.class
      end
      
      def eql?(other)
        self == other
      end
      
      def hash
        self.class.hash
      end
      
      def read_format
        raise NotImplementedError.new
      end
      private :read_format
    end
    
    class WavContainer
      include Container
      extension ".wav"
      extension ".wave"
      
      def read_format
        format_extractor = MicrosoftWavPcmFormatExtractor.new
        format_extractor.read_all open(@filepath, "r")
        
        fmt = format_extractor.format
        if fmt.format == 1
          result = Formats::PcmWavFormat.new
          result.bits_per_sample = fmt.bits_per_sample
          result.sample_rate = fmt.sample_rate
          result.num_channels = fmt.num_channels
          result
        else
          raise RuntimeError.new("unsupported wav format: #{fmt}")
        end
      end
    end
    
    class ApeContainer
      include Container
      extension ".ape"
      
      def read_format
        Formats::ApeFormat.new
      end
    end
    
    class WavpackContainer
      include Container
      extension ".wv"
      
      def read_format
        Formats::WavpackFormat.new
      end
    end
    
    class FlacContainer
      include Container
      extension ".flac"
      
      def read_format
        Formats::FlacFormat.new
      end
    end
    
    class Mp3Container
      include Container
      extension ".mp3"
      
      def read_format
        Formats::Mp3Format.new
      end
    end
    
    class M4aContainer
      include Container
      extension ".m4a"
      
      def read_format
        raise NotImplementedError.new
      end
    end
    
    class OggContainer
      include Container
      extension ".ogg"
      
      def read_format
        raise NotImplementedError.new
      end
    end
  end
    
  module Formats
    module Matcher
      def ==(other)
        self.class == other.class
      end
      
      def eql?(other)
        self == other
      end
    end
        
    module Format
      def hash
        self.class.hash
      end
      
      def self.included(othermod)
        othermod.extend(ClassMethods)
      end
      
      module ClassMethods
        def matcher
          new.extend(Matcher)
        end
      end
    end
    
    class PcmWavFormat < Struct.new(:bits_per_sample, :num_channels, :sample_rate, :floating_point)
      include Format
      
      def block_align
        num_channels * bits_per_sample / 8
      end
      
      def byte_rate
        sample_rate * num_channels * bits_per_sample / 8
      end
      
    end
    
    class ApeFormat
      include Format
    end
    
    class WavpackFormat
      include Format
    end
    
    class FlacFormat
      include Format
    end
    
    class Mp3Format
      include Format
    end
    
    class AacFormat
      include Format
    end
    
    class AlacFormat
      include Format
    end
    
    class OggVorbisFormat
      include Format
    end
    
    class MultiTrackFormat
      include Format
      # for future use
    end
  end
    
end